#pragma once
#include <iostream>
#include <memory>
#include <sys/select.h>
#include "Socket.hpp"

namespace wxd
{
#define FD_NUM sizeof(fd_set) * 8
    const int default_fd = -1;
    class SelectServer
    {
    private:
        int AddFds(fd_set &readfds)
        {
            int maxfd = default_fd;
            // 遍历数组，添加
            for (size_t i = 0; i < FD_NUM; i++)
            {
                if (_fd_array[i] == default_fd)
                    continue;
                FD_SET(_fd_array[i], &readfds);
                if (_fd_array[i] > maxfd)
                {
                    maxfd = _fd_array[i];
                }
            }

            return maxfd;
        }

    public:
        SelectServer(u_int16_t port)
            : _port(port), _listen_socket_ptr(std::make_unique<TcpSocket>(port))
        {
            // 初始化_fd_array 全部为-1
            for (int i = 0; i < FD_NUM; i++)
            {
                _fd_array[i] = default_fd;
            }
        }
        void Init()
        {
            // 完成创建socket,bind,listen
            _listen_socket_ptr->TcpSocketModule();
            // 将listensockfd添加到集合
            _fd_array[0] = _listen_socket_ptr->Fd();
        }

        void Start()
        {
            // 调用select帮助我们检测文件描述符是否就绪
            fd_set readfds;
            while (true)
            {
                // 重置readfds，因为每次返回的都是就绪的文件描述符
                FD_ZERO(&readfds);
                // 添加fd，返回最大fd
                int maxfd = AddFds(readfds);
                // LOG(LogLevel::DEBUG)<<"maxfd = "<<maxfd;
                // FD_SET(_listen_socket_ptr->Fd(),&readfds);

                timeval timeout = {10, 0};
                int n = select(maxfd + 1, &readfds, nullptr, nullptr, &timeout);
                // 对select的结果进行判断
                switch (n)
                {
                // timeout
                case 0:
                    LOG(LogLevel::INFO) << "timeout...  [" << timeout.tv_sec << ":" << timeout.tv_usec << "]";
                    break;
                // 发生错误
                case -1:
                    LOG(LogLevel::ERROR) << "select error!";
                    return;
                    break;
                default:
                    // 有文件描述符就绪
                    LOG(LogLevel::INFO) << "有文件描述符就绪！";

                    // 分派任务
                    Dispatcher(readfds);
                    break;
                }
            }
        }

        void Accepter()
        {
            InetAddrTrans client_trans;
            // 此时一定不会阻塞，读取新的连接
            int newfd = _listen_socket_ptr->Accept(client_trans);
            if (newfd < 0)
                return;
            LOG(LogLevel::INFO) << "收到一个连接[" << client_trans.AddrInfo() << "]";
            // 把连接添加进文件描述符集
            int pos = 0;
            for (int i = 0; i < FD_NUM; i++)
            {
                if (_fd_array[i] == default_fd)
                {
                    pos = i;
                    // 填入
                    _fd_array[i] = newfd;
                    break;
                }
                if (pos == -1)
                {
                    LOG(LogLevel::ERROR) << "服务器已经满了";
                    close(newfd);
                }
            }
        }

        void Recv(int readfd)
        {
            char buffer[1024];
            int n = recv(readfd, buffer, sizeof(buffer) - 1, 0);
            // bug 暂没有考虑过读取完整性的问题 -> 应用层制定协议
            if (n > 0)
            {
                buffer[n] = 0;
                LOG(LogLevel::INFO) << "收到消息" << buffer;
                // 回显消息
                std::string message = "echo# ";
                message += buffer;
                send(readfd, message.c_str(), message.size(), 0); // bug暂未考虑过输出条件是否就绪的问题，写poll的时候考虑
            }
            else if (n == 0)
            {
                LOG(LogLevel::INFO) << "客户端退出... fd = " << readfd;
                // 关闭客户端的文件描述符
                close(readfd);
                for (int i = 0; i < FD_NUM; i++)
                {
                    if (_fd_array[i] == readfd)
                    {
                        _fd_array[i] = default_fd;
                        break;
                    }
                }
                close(readfd);
            }
            else
            {
                LOG(LogLevel::ERROR) << "读取出错！";

                for (int i = 0; i < FD_NUM; i++)
                {
                    if (_fd_array[i] == readfd)
                    {
                        _fd_array[i] = default_fd;
                        break;
                    }
                }
                close(readfd);
                return;
            }
        }

        // 分派任务
        void Dispatcher(fd_set &readfds)
        {
            // 处理就绪的文件描述符
            // 存在 且 就绪
            for (int i = 0; i < FD_NUM; i++)
            {
                // LOG(LogLevel::DEBUG)<<_fd_array[i];
                if (_fd_array[i] == default_fd)
                    continue;

                // 如果是listensockfd,并且就绪,处理accept任务
                if (_fd_array[i] == _listen_socket_ptr->Fd() && FD_ISSET(_fd_array[i], &readfds))
                {
                    Accepter();
                }

                // 不是如果是listensockfd，但是就绪
                if (_fd_array[i] != _listen_socket_ptr->Fd() && FD_ISSET(_fd_array[i], &readfds))
                {
                    LOG(LogLevel::DEBUG) << "1111";
                    // 处理收发任务
                    Recv(_fd_array[i]);
                }
            }
        }

    private:
        u_int16_t _port;
        std::unique_ptr<Socket> _listen_socket_ptr;
        int _fd_array[FD_NUM]; // 文件描述符集
    };

}